package model

import (
	"crypto/md5"
	"encoding/hex"
	"errors"
	"github.com/dailing/levlog"
	"github.com/satori/go.uuid"
	"io/ioutil"
	"mime"
	"os"
	"path"
	"path/filepath"
)

var BinaryStorage = "/upload"

type BinObject struct {
	Id          int64  `json:"-"`
	UUID        string `xorm:"unique 'uuid'" json:"-"`
	Name        string `json:"name"`
	Ext         string `json:"ext"`
	Type        string `json:"type"`
	ContentType string `json:"content_type"`
	Md5Hash     string `json:"md_5_hash"`
	Payload     []byte `xorm:"-" json:"-"`
	Url         string `xorm:"-" json:"url"`
	Uploader    int64  `json:"-"`
}

func (b *BinObject) Delete() error {
	panic("implement me")
}

func (b *BinObject) Get() error {
	_, err := engine.Get(b)
	levlog.E(err)
	return err
}

func (b *BinObject) Update() error {
	panic("implement me")
}

func (b *BinObject) Insert() error {
	return b.Save()
}

func (b *BinObject) SetOwner(userId int64) {
	b.Uploader = userId
}

func (b *BinObject) GetOwner() int64 {
	return b.Uploader
}

// this function calculate md5 and set all requited values, and insert sql records
func (b *BinObject) Save() error {
	uid, err := uuid.NewV4()
	if err != nil {
		levlog.E(err)
		return err
	}
	b.UUID = uid.String()
	if len(b.Payload) <= 0 {
		return errors.New("empty content")
	}
	// calculate Md5Hash
	h := md5.Sum(b.Payload)
	hstr := hex.EncodeToString(h[:])
	if b.Md5Hash != "" && b.Md5Hash != hstr {
		return errors.New("hash not match")
	}
	b.Md5Hash = hstr
	// set ext
	if b.Ext == "" {
		b.Ext = filepath.Ext(b.Name)
	}
	// set ContentType
	if b.ContentType == "" {
		b.ContentType = mime.TypeByExtension(b.Ext)
	}
	// save files
	if err := ioutil.WriteFile(
		path.Join(BinaryStorage, b.UUID), b.Payload, os.ModePerm); err != nil {
		levlog.E(err)
		return err
	}
	// insert records
	if _, err := engine.Insert(b); err != nil {
		levlog.E(err)
		return err
	}
	return nil
}

func GetBinaryObj(uuid string) (*BinObject, error) {
	var err error
	bo := &BinObject{
		UUID: uuid,
	}
	// read record
	if _, err = engine.Get(bo); err != nil {
		levlog.E(err)
		return nil, err
	}
	// read file
	if bo.Payload, err = ioutil.ReadFile(filepath.Join(BinaryStorage, bo.UUID)); err != nil {
		levlog.E(err)
		return nil, err
	}
	return bo, nil
}

func GetBinaryObjByID(id int64) (*BinObject, error) {
	var err error
	bo := &BinObject{
		Id: id,
	}
	// read record
	if _, err = engine.Get(bo); err != nil {
		levlog.E(err)
		return nil, err
	}
	// read file
	if bo.Payload, err = ioutil.ReadFile(filepath.Join(BinaryStorage, bo.UUID)); err != nil {
		levlog.E(err)
		return nil, err
	}
	return bo, nil
}
func NewBinaryObj(name, ext, tp, md5hash, ctype string, payload []byte) *BinObject {
	return &BinObject{
		Name:        name,
		Ext:         ext,
		Type:        tp,
		Payload:     payload,
		Md5Hash:     md5hash,
		ContentType: ctype,
	}
}
